「尚未接受任何來源,匯出內容不會包含可驗證引用。」
這是 ResearchForge 早期引用功能裡的一則提醒。
但沿著當時的下載程式繼續往下看,會發現一件有些矛盾的事:系統顯示完這則警告之後,下一步仍然可以直接匯出檔案。
換句話說,系統已經知道來源存在缺口,卻還沒有因此把出口關上。[2][3]
上一篇談的是模板與匯出:一份文件能被交出去,不代表研究已經完成。
這一篇,我想把問題再往前推一步。
即使系統已經找到來源、整理好參考資料,甚至在每個句子後面放上正式的引用,我還是得繼續問:
那個引用,真的支持前面的那句話嗎?
要回答這個問題,不能只看引用格式漂不漂亮,而必須先弄清楚:ResearchForge 當時到底保存了什麼資料,又真正檢查到了哪一層。
引用最容易造成的一種錯覺,是「看起來有根據」。
一個句子可以文法正確、前後連貫,句尾也附著一個正式的 [1]。但文末列出一篇論文,只代表讀者能找到那個來源;句尾出現引用標記,也不代表來源內容真的支持這個句子的全部主張。
可以用一個假設例子理解。
假設某份研究只記錄:
某款工具在特定任務中的表現有所提升。
報告卻把它寫成:
這款工具適合所有工作。
即使引用確實指向那份研究,這個句子的範圍仍然超出了原始材料。
問題不在於「引用是不是假的」,而在於來源提供的證據範圍,沒有大到足以支撐報告做出的結論。
這不是 ResearchForge 當時某一次真實報告中實測出的錯誤,而是我用來區分兩件事情的例子:
ResearchForge 在這個階段,首先處理的是比較基礎的前者。
2026 年 6 月 12 日的 citation foundation milestone 加入了來源搜尋、共同資料格式與來源審查狀態,讓來源不再只是報告最後附上的一行文字,而是正式進入系統資料流程中的一筆紀錄。[1]
這還不是逐句證據查核。
但至少從這裡開始,我可以回答一個以前不容易回答的問題:
「這份報告目前到底用了哪一筆來源?」
當時的搜尋程式接上 Crossref 與 Semantic Scholar。
兩個服務回傳的資料格式並不完全相同,因此 ResearchForge 會先把搜尋結果整理成共同的 SourceRecord,也就是一筆標準化的來源紀錄。
其中包含標題、作者、年份、摘要、DOI、網址、來源服務,以及審查狀態等欄位;前後端都依照同一套欄位約定處理。[1]
這件事乍看只是資料整理,但其實很重要。
如果不同搜尋服務回傳的作者、年份、網址都用完全不同的結構表示,那麼後面的審查、引用、匯出甚至錯誤追查,都會被資料格式本身拖住。
共同格式至少先讓每一筆來源有一致的「身分」。
DOI 在這裡主要用來識別文獻。
至於標題、作者、年份等資訊,通常統稱為 metadata,也就是「中繼資料」。它們可以幫助系統辨認與呈現一篇來源,卻不能取代來源實際寫了什麼。
知道一篇文章叫什麼,和知道它支持什麼,是兩件完全不同的工作。
共同資料格式解決的是前者。
例如審查畫面可以用同一套欄位顯示來源,而不必因為資料來自 Crossref 或 Semantic Scholar,就各自另外寫一套作者、年份或標題的處理邏輯。
當時的介面也已經可以編輯標題、作者、年份等資料,並把來源標記為接受或拒絕。[1][3]
這讓來源開始變成可以被單獨檢查、修改與追蹤的對象,而不是直接混進整篇報告裡。
建立共同資料格式之後,還有另一個很容易踩到的問題:
如果來源資料不完整,系統要不要幫它補完整?
ResearchForge 當時的做法,是不要猜。
歷史程式會把沒有取得的年份、摘要或 DOI 保留為 null;如果沒有作者,則保留空清單。[1]
整理程序可以清除多餘空白、統一資料形狀,但不會因為欄位看起來應該存在,就自己推測一個作者、年份或 DOI 填進去。
這個設計原則後來對我很重要:
共同格式的目的,不是讓每一格看起來都有資料,而是讓缺口也能被明確表示。
如果不知道,就留下不知道。
否則資料結構雖然變完整了,證據反而可能變得更不可信。
accepted 代表「被選用」,不是「已被證明」來源進入系統後,還需要經過審查。
搜尋回來的來源預設會是 pending,也就是「待審查」。
使用者之後可以把它改成:
accepted:接受;rejected:拒絕。當時的前端會先挑出 accepted 的來源,再放進報告生成輸入;參考資料的組裝也會篩選這個狀態。[1][2][3]
下面是當時共同來源模組中的原始程式節錄:
export function acceptedSourceRecords(sources: SourceRecord[]): SourceRecord[] {
return sources.filter((source) => source.reviewState === "accepted");
}
export function rejectedSourceRecords(sources: SourceRecord[]): SourceRecord[] {
return sources.filter((source) => source.reviewState === "rejected");
}
filter 會保留符合條件的資料。
第一個函式只留下 accepted 的來源;第二個則只留下 rejected 的來源。pending 不會通過其中任何一個。
但真正重要的是:這段程式只讀了 reviewState。
它沒有查看摘要。
沒有把來源原文和報告句子逐句比較。
也沒有判斷「這篇論文是不是足以支持這個結論」。
所以:
accepted的真正意思,是「這筆來源在目前工作流程中被選用了」。
而不是:
「這份來源已經證明所有引用它的句子都正確。」
這兩句話看起來只差一點,實際上卻是完全不同的品質承諾。
這也讓我開始重新注意介面上的措辭。
「已接受來源」可以描述一個操作狀態。
但如果介面把它寫成「內容已驗證」,就代表系統承諾了程式根本沒有完成的檢查。
現在回頭看,我會先問:
這個狀態到底是由哪一個動作產生的?
再決定能不能把它當成品質標籤。
早期 ResearchForge 並沒有把「來源被接受」和「來源資料完整」綁在一起。
即使一筆來源已經是 accepted,它仍然可能缺少作者、年份、DOI 或網址。
參考資料格式化時,程式會標出缺少的欄位;相關測試也刻意建立缺少作者與年份的測試資料,用來確認系統會保留「未提供」的狀態,而不是自行補造內容。[2]
這些測試可以證明一些很具體的事情:
但它們不能證明:
因為這些測試檢查的是資料處理規則,不是來源與主張之間的語意支持關係。[1][2]
這也是為什麼文章開頭那則匯出提醒值得特別注意。
當時的匯出檢查會確認幾件事情:
以 Markdown 下載流程為例,程式會先呼叫提醒函式,然後繼續呼叫下載函式;生成按鈕本身,也沒有因為來源不足而被停用。[2][3]
所以那個流程實際上是:
系統知道這份報告可能還有問題 → 顯示警告 → 仍然允許匯出。
這不是提醒失敗。
提醒其實很誠實。
真正的問題是:
提醒和阻擋,本來就是兩種不同的機制。
提醒的作用是告訴使用者:
這裡還有事情沒有完成。
阻擋的作用則是決定:
如果這件事情沒有完成,系統還允不允許流程繼續。
如果只確認「有沒有警告」,很容易誤以為風險已經被處理。
但對研究工具來說,更重要的問題往往是:
當風險真的存在時,出口到底有沒有被關上?
還有一個不能被功能名稱掩蓋的邊界。
當時 ResearchForge 的報告生成器,仍然是依規則組裝內容的模擬實作,並不是一套已經讓語言模型完整閱讀文獻、再逐句驗證主張的系統。[3]
因此,這個階段真正完成的是:
來源正式進入審查與報告資料流。
而不是:
自動研究與證據驗證已經完成。
兩者距離仍然很遠。
不過現在回頭看,我仍然認為先把來源變成可追蹤的共同紀錄是必要的。
因為如果有一天,一筆不應該出現的來源進入了參考資料,我至少可以沿著資料流往回查:
這是哪一筆 SourceRecord?
它原本是 pending、accepted 還是 rejected?
哪一段程式篩選了它?
哪一個流程把它送進報告?
哪一個步驟產生了參考資料?
至少問題開始有一條可以追蹤的路。
至於更困難的那一層——
這份來源到底有沒有支持報告裡的那個句子?
仍然必須另外核對。
不能期待一個 accepted 狀態,替整個證據鏈回答所有問題。
如果你也正在做自己的 AI 報告工具,我覺得可以先不用急著設計一個「可信度分數」。
先隨便挑一筆來源,沿著它把整條資料流走一次。
先看:
搜尋完成後,系統到底保存了哪些欄位?
哪些欄位是真的從來源取得,哪些只是 metadata?
接著看:
誰把它從 pending 改成 accepted?
這個接受動作到底代表什麼?
再往後看:
哪些輸出真的有讀取 reviewState?
被拒絕的來源是否確實消失?
缺失欄位是否真的保持缺失?
最後,再挑報告中的一句話。
回到原始材料裡,確認來源真正能支持到哪一個範圍。
如果找不到對應內容,就把這個缺口留下來。
不要因為句尾已經有一個漂亮的 [1],就讓引用格式替證據本身背書。
這是我從 ResearchForge 早期實作整理出來的一套追查方法,而不是它當時已經具備的完整防護。
但我慢慢發現,這種追查方式比直接問「系統到底準不準」更有用。
因為每一個暫時答不出來的問題,都可以被拆成下一個明確的工程工作:
來源沒有身分,就先建立來源紀錄。
審查狀態語意不清,就先重新定義狀態。
缺口只有提醒、沒有阻擋,就決定哪些條件應該成為 gate。
主張和來源沒有建立對應,就再往證據驗證那一層前進。
不需要一開始就把整份 AI 報告當成一個難以理解的黑箱。
當來源終於能被辨認、整理與追蹤之後,下一個問題也自然出現了:
進入審查清單裡的這些資料,一開始究竟是怎麼被找到的?
下一篇,我會沿著查詢流程再往前追。
從一個搜尋框開始,看看為什麼 ResearchForge 後來需要的不只是「搜尋得到結果」,而是一條能夠診斷、能夠知道自己在哪裡失敗的搜尋流程。